// TcpServer类定义  单例模式  懒汉模式 
// 基本网路接口封装  套接字  绑定  监听  


/*
- 文件名尽量全部小写, 可以包含下划线 (_) 
- 变量名使用小写字母，单词之间使用下划线分隔。
- 函数名使用驼峰命名法，即首字母小写，后面的单词首字母大写，不使用下划线。
- 类名使用驼峰命名法，即每个单词首字母大写，不使用下划线。
- 常量名使用全大写字母，单词之间使用下划线分隔。
*/
#pragma once
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>

#include "log.hpp"

#define BACKLOG 5   // 宏观理解为允许 listen -> accept 的用户数  ？？？

class TcpServer
{
    //成员变量
    private: 
    int _port = 8080 ;          //端口号  默认8080
    int _listen_sock ;          //服务端总监听套接字
    static TcpServer* _ser ;    //静态成员变量必须在类外初始化 ——> 调用类内函数实现单例模式

    //单例模式私有构造函数
    private:

    TcpServer(int port = 8080)
    :_port(port)
    ,_listen_sock(-1)
    {

    }

    //防拷贝->实现单例
    TcpServer(const TcpServer* ser)
    {
        
    }

    public:

    // 单例获取函数
    static TcpServer* getinstance(int port = 8080)
    {
        // 静态初始化互斥量
        static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER ;
        if(nullptr == _ser)
        {
            pthread_mutex_lock(&lock) ;
            
            if(nullptr == _ser)
            {
                _ser = new TcpServer(port) ;
                _ser->initServer(); 
            }   

            pthread_mutex_unlock(&lock) ;
        }

        return _ser ; 
    }

    //socket 
    void socketPacked()
    {
        _listen_sock = socket(AF_INET, SOCK_STREAM, 0) ; 
        if(0 > _listen_sock)
        {
            // 创建套接字失败
            LOG(FATAL, "create sock error");
            exit(-1);
        }
        //存放setsockopt选项设置值  ？？？
        int opt = 1 ; 
        //- SOL_SOCKET：通用套接字选项           ？？？
        //- SO_REUSEADDR：允许重用本地地址和端口  ？？？
        setsockopt(_listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

        LOG(INFO, "crate sock sucess") ;

    }

    //
    void bindPacked()
    {
        sockaddr_in local ; 
        memset(&local, 0, sizeof(local));
        // 协议 IPV4
        local.sin_family = AF_INET ;
        // IP
        //INADDR_ANY表示希望绑定到本地的任意IP地址
        //云服务器不能直接绑定公网IP! ! !
        local.sin_addr.s_addr = INADDR_ANY ; 
        //端口
        local.sin_port = htons(_port) ;

        bind(_listen_sock,(struct sockaddr*) &local, sizeof(local)) ; 
        LOG(INFO, "bind sock success") ;
    }

    void listenPacked()
    {
        if(0 > listen(_listen_sock, BACKLOG))
        {
            //监听套接字失败
            LOG(FATAL, "listen sock failed") ;
            exit(3);
        } 
        LOG(INFO, "linten sock sucess");
    }

    int acceptPacked(std::string *clientip, uint16_t *clientport, int* err)
    {
        sockaddr_in temp ; 
        socklen_t length = sizeof(temp) ; 
        int sock = accept(_listen_sock, (sockaddr*)&temp, &length) ; 
        *err = errno ; 
        if(sock < 0)
        {
            LOG(WARNING, "accepte error ------ may be onceloop normal over") ;
        }
        else
        {
            /*
            inet_ntoa 函数：这个函数用于将网络字节序中的 IPv4 地址转换为点分十进制的字符串表示形式。
            它接受一个类型为 struct in_addr 的参数，返回一个指向表示 IP 地址的字符串的指针。

            ntohs 函数：这个函数用于将网络字节序中的 16 位数（如端口号）
            从网络字节序（大端序）转换为主机字节序（可能是大端或小端，取决于系统）。
            它接受一个 uint16_t 类型的参数，返回一个主机字节序表示的端口号。
            */
            *clientip = inet_ntoa(temp.sin_addr) ; 
            *clientport = ntohs(temp.sin_port) ; 
        }

        return sock ; 
    }

    void initServer()
    {
        socketPacked();
        bindPacked();
        listenPacked();
        LOG(INFO, "init server sucess") ;
    }

    ~TcpServer()
    {
        if(0 < _listen_sock)
        {
            close(_listen_sock) ;
        }
    }

    int get_sock()
    {
        return _listen_sock ; 
    }

};


//类外初始化静态成员

 TcpServer* TcpServer::_ser = nullptr ; 







